<?php
class PlacementField extends TextField {
	protected $extraClasses = array('dropdown' => 'dropdown');
	
	private static $allowed_actions = array(
        'check_placement',
        'check_position'
    );
	
	protected $customPosition = array(
		'Manual Left' => array(
			'Value' => 1,
			'Title' => 'Manual Left'
		),
		'Manual Right' => array(
			'Value' => 2,
			'Title' => 'Manual Right'
		),
	);
	
	protected $positionValue;
	
	/**
     * The url to use as the check placement and position
     * @var string
     */
    protected $checkPlacementURL, $checkPositionURL;
	
	/**
	 * @var UsernameField
	 */
	protected $usernameField = null;
	
	/**
	 * @var NameField
	 */
	protected $nameField = null;
	
	/**
	 * @var IDField
	 */
	protected $idField = null;
	
	/**
	 * @var PositionField
	 */
	protected $positionField = null;
	
	protected $excludeID = 0;
	
	public function __construct($name, $title = null, $value = "", $positionValue = ""){
		Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
		Requirements::javascript(FRAMEWORK_DIR . '/javascript/i18n.js');
		Requirements::add_i18n_javascript('network/javascript/lang');
        Requirements::javascript('network/javascript/PlacementField.min.js');
		Requirements::css('network/css/PlacementField.css');
		
		$this->positionValue = $positionValue;
		
		$this->usernameField = TextField::create($name . '[username]', false);
		$this->nameField = TextField::create($name . '[name]', false);
		$this->idField = HiddenField::create($name . '[id]');
		
		$position = array_merge(singleton('Placement')->dbObject('SetOption')->enumValues());
		foreach($this->customPosition as $data){
			$position[$data['Value']] = $data['Title'];
		}
		foreach($position as $key => $val){
			$position[$key] = _t(sprintf('%s.%s', 'Placement', strtoupper(preg_replace("/[^a-z0-9_]+/i", "", str_replace(' ', '_', $val)))), $val);
		}
		$this->positionField = DropdownField::create($name . '[position]', false, $position)->setEmptyString(_t('PlacementField.PLEASE_SELECT', 'Please select...'));
		
		parent::__construct($name, $title, $value);
	}
	
	function Field($properties = array()) {
		$this->usernameField->setAttribute('rel', 'placement-username');
		$this->usernameField->setAttribute('data-url', $this->getCheckPlacementURL());
		$this->usernameField->setAttribute('autocomplete', 'off');
		if ($this->Required()) {
			$this->usernameField->setAttribute('required', 'required');
			$this->usernameField->setAttribute('aria-required', 'true');
		}
		$this->nameField->setAttribute('rel', 'placement-name');
		$this->nameField->setAttribute('readonly', 'readonly');
		$this->nameField->setAttribute('autocomplete', 'off');
		$this->nameField->setAttribute('tabindex', '-1');
		$this->idField->setAttribute('rel', 'placement-id');
		$this->idField->setAttribute('autocomplete', 'off');
		$this->positionField->setAttribute('rel', 'placement-position');
		$this->positionField->setAttribute('data-url', $this->getCheckPositionURL());
		$this->positionField->setAttribute('autocomplete', 'off');
		if ($this->Required()) {
			$this->positionField->setAttribute('required', 'required');
			$this->positionField->setAttribute('aria-required', 'true');
		}
        return sprintf('%s %s %s %s', $this->usernameField->Field(), $this->nameField->Field(), $this->idField->Field(), $this->positionField->Field());
    }
	
	function Type() {
		return 'placement text';
	}
	
	/**
	 * Set the field value.
	 * 
	 * @param mixed $value
	 * @return FormField Self reference
	 */
	public function setValue($value) {
		if(is_array($value)) {
			$this->value = array(
				'username' => $value['username'],
				'position' => $value['position']
			);
		} else {
			$this->value = array(
				'username' => $value,
				'position' => $this->positionValue
			);
		}
		
		$this->usernameField->setValue($this->value['username']);
		if($member = Distributor::get_obj_by_username($this->value['username'])){
			$this->nameField->setValue($member->getName());
			$this->idField->setValue($member->ID);
		}
		else{
			$this->nameField->setValue('');
			$this->idField->setValue('');
		}
		
		$this->positionField->setValue($this->value['position']);
		return $this;
	}
	
	public function setDisabled($bool) {
		parent::setDisabled($bool);
		$this->usernameField->setDisabled($bool);
		$this->nameField->setDisabled($bool);
		$this->positionField->setDisabled($bool);
		return $this;
	}

	public function setReadonly($bool) {
		parent::setReadonly($bool);
		$this->usernameField->setReadonly($bool);
		$this->positionField->setReadonly($bool);
		return $this;
	}
	
	/**
	 * Method to save this form field into the given data object.
	 * By default, makes use of $this->dataValue()
	 * 
	 * @param DataObjectInterface $record DataObject to save data into
	 */
	public function saveInto(DataObjectInterface $record) {
		$var = $this->dataValue();
		$record->setCastedField($this->getName(), array('username' => $var['username'], 'position' => $var['position']));
		$record->setCastedField('SetPlacementUsername', $var['username']);
		
		if(preg_match('/^[0-9]+$/', $var['position'])){
			$record->setCastedField('SetPlacementPosition', $var['position']);
		} else {
			$record->setCastedField('SetPlacementPositionOption', $var['position']);
		}
	}

	/**
	 * Makes a read only field
	 *
	 * @return ReadonlyField
	 */
	/**
	 * Returns a readonly version of this field
	 */
	public function performReadonlyTransformation() {
		$copy = $this->castedCopy('PlacementField_Readonly');
		$copy->setReadonly(true);
		$copy->usernameField = $this->usernameField->performReadonlyTransformation();
		$copy->usernameField->setReadonly(true);
		$copy->nameField = $this->nameField->performReadonlyTransformation();
		$copy->nameField->setReadonly(true);
		$copy->idField = $this->idField->performReadonlyTransformation();
		$copy->idField->setReadonly(true);
		$copy->positionField = $this->positionField->performReadonlyTransformation();
		$copy->positionField->setReadonly(true);
		return $copy;
	}
	
	/**
     * Set exclude id.
     * 
     * @param Int.
     */
    public function setExcludeID($id) {
        $this->excludeID = $id;
		return $this;
    }
	
	/**
     * Get exclude id.
     *  
     * @return Int.
     */
    public function getExcludeID() {
		return $this->excludeID;
    }
	
	/**
     * Set position form field.
     * 
     * @param FormField.
     */
    public function setPositionField($field) {
        $this->positionField = $field;
		return $this;
    }
	
	/**
     * Get position form field
     *  
     * @return FormField.
     */
    public function getPositionField() {
		return $this->positionField;
    }
	
	/**
     * Set the URL used to check placement.
     * 
     * @param string $URL The URL used for check placement.
     */
    public function setCheckPlacementURL($URL) {
        $this->checkPlacementURL = $url;
    }
	
	/**
     * Get the URL used to check placement. Returns null
     * if the built-in mechanism is used.
     *  
     * @return The URL used for check placement.
     */
    public function getCheckPlacementURL() {

        if (!empty($this->checkPlacementURL))
            return $this->checkPlacementURL;

        // Attempt to link back to itself
        return $this->Link('check_placement');
    }
	
	/**
     * Set the URL used to check placement position.
     * 
     * @param string $URL The URL used for check placement position.
     */
    public function setCheckPositionURL($URL) {
        $this->checkPositionURL = $url;
    }
	
	/**
     * Get the URL used to check placement position. Returns null
     * if the built-in mechanism is used.
     *  
     * @return The URL used for check placement position.
     */
    public function getCheckPositionURL() {

        if (!empty($this->checkPositionURL))
            return $this->checkPositionURL;

        // Attempt to link back to itself
        return $this->Link('check_position');
    }
	
    function validate($validator) {
    	$var = $this->Value();
        $id = Distributor::get_id_by_username($var['username']);

        if(!$id) {
            $validator->validationError($this->name, _t('PlacementField.PLACEMENT_USERNAME_INVALID', "The placement username is invalid"));
            return false;
        } else {
			$position = preg_match('/^[0-9]+$/', $var['position']) ? (int)$var['position'] : 0;
			if($position){
				$checkPosition = Placement::get()
				->filter('ParentID', (int)$id)
				->filter('NSeqno', $position)
				->exclude('ID', (int)$this->getExcludeID())
				->count();
				if($checkPosition) {
		            $validator->validationError($this->name, _t('PlacementField.POSITION_EXISTS', "The placement position is already registered by other member"));
		            return false;
		        }
			}
        }

        return true;
    }
	
	/**
     * Handle a request for a placement checking.
     * 
     * @param HTTPRequest $request The request to handle.
     * @return Placement data result
     */
    function check_placement(HTTPRequest $request) {
    	$data = array('result' => 'error', 'id' => '', 'name' => '', 'msg' => _t('PlacementField.PLACEMENT_USERNAME_INVALID', "The placement username is invalid"));
		$placement = Placement::get()->filter('Member.Username', $request->getVar('username'))->first();
		if($placement){
			if($obj = Placement::get()->find('MemberID', $request->getVar('sponsor_id'))){
				$checkPlacement = Placement::get()
		    	->filter('NLeft:GreaterThanOrEqual', (int)$obj->NLeft)
		    	->filter('NRight:LessThanOrEqual', (int)$obj->NRight)
				->filter('MemberID', (int)$placement->MemberID)
				->count();
				if($checkPlacement){
					$data = array('result' => 'success', 'id' => $placement->Member()->ID, 'name' => $placement->Member()->getName(), 'msg' => _t('PlacementField.PLACEMENT_USERNAME_VALID', "The placement username is valid"));
				}
			}
			else{
				$data = array('result' => 'success', 'id' => $placement->Member()->ID, 'name' => $placement->Member()->getName(), 'msg' => _t('PlacementField.PLACEMENT_USERNAME_VALID', "The placement username is valid"));
			}
		}

        // the response body
        return Convert::array2json($data);
    }
	
	/**
     * Handle a request for placement position checking.
     * 
     * @param HTTPRequest $request The request to handle.
     * @return Valid placement position result
     */
    function check_position(HTTPRequest $request) {
    	$data = array('result' => 'error', 'msg' => _t('PlacementField.POSITION_EXISTS', "The placement position is already registered by other member"));
		$position = preg_match('/^[0-9]+$/', $request->getVar('position')) ? (int)$request->getVar('position') : 0;
		$checkPosition = Placement::get()
		->filter('ParentID', (int)$request->getVar('placement_id'))
		->filter('NSeqno', $position)
		->exclude('ID', (int)$this->getExcludeID())
		->count();
				
		if(!$position || !$checkPosition){
			$data = array('result' => 'success', 'msg' => _t('PlacementField.PLACEMENT_USERNAME_VALID', "The placement position is valid"));
		}
        // the response body
        return Convert::array2json($data);
    }
}

/**
 * Readonly version of {@link PlacementField}.
 * Allows placement info to be represented in a form, by showing in a user friendly format.
 * @package network
 * @subpackage placement-field
 */
class PlacementField_Readonly extends PlacementField {
	/**
	 * Include a hidden field in the HTML for the readonly field
	 * @var boolean
	 */
	protected $includeHiddenField = false;

	/**
	 * If true, a hidden field will be included in the HTML for the readonly field.
	 * 
	 * This can be useful if you need to pass the data through on the form submission, as
	 * long as it's okay than an attacker could change the data before it's submitted.
	 *
	 * This is disabled by default as it can introduce security holes if the data is not
	 * allowed to be modified by the user.
	 * 
	 * @param boolean $includeHiddenField
	 */
	public function setIncludeHiddenField($includeHiddenField) {
		$this->includeHiddenField = $includeHiddenField;
	}
	
	public function Field($properties = array()) {
		$value = $this->dataValue();
		$text = sprintf('%s - %s', $value['username'], $this->nameField->dataValue());
		$copy = $this->castedCopy('ReadonlyField')->setReadonly(true)->setTitle($this->title)->setValue($text);
		// Include a hidden field in the HTML
		if($this->includeHiddenField && $this->readonly) {
			return $copy->Field($properties) . $this->usernameField->performReadonlyTransformation()->setReadonly(false)->Field($properties) . $this->nameField->performReadonlyTransformation()->setReadonly(false)->Field($properties) . $this->idField->performReadonlyTransformation()->setReadonly(false)->Field($properties) . '<br>' . $this->positionField->performReadonlyTransformation()->Field($properties);
		} else {
			if(isset($value['position']) && $value['position']){
				$source = $this->positionField->getSource();
				$text .= ' (' . $source[$value['position']] . ')';
			}
			return $copy->setValue($text)->Field($properties);
		}
	}
}
?>
